/*
 * BRAIN10 file that initiates the brain and virtual brain
 * machines.  Used for allocating memory for instructions.
 * As well as interpreting instructions.
 * 
 * BRAIN10.c
 */

#include <stdio.h>
#include <stdlib.h>
#include "mailman.h"
#include "semaphore.h"
#include "BRAIN10.h"
#include "instructions.h"
#include "scheduler.h"

/*
 * Initiate the BRIAN machine.
 */
void initiateBrain(BRAIN10 *brain10)
{
    int i,j;
    brain10 -> dataLine = 0;
    brain10 -> currentProcess = 0;
    brain10 -> numProcesses = 0;
    
    for (i = 0; i < 10000; i++)
    {
        brain10 -> storage[i][0] = '#';
    }
    
    for (i = 0; i < 100; i++)
    {
        brain10 -> readyQueue[i] = -1;
    }
    int counter = 0;
    // Initializes the PLB (Master Page Table).
    for (i=0; i<100; i++)
    {
        for (j=0; j<10; j++)
            brain10 -> PLB[i][j] = counter++;       
    }
    
}

/*
 * Initiate the virtual BRAIN machines.
 */
void initiateVBrain(VBRAIN10 *vbrain10)
{
    int i, j;
    for (i = 0; i < 20; i++)
    {
        vbrain10 -> vC[i] = 0;
        vbrain10 -> vIC[i] = 0;
        vbrain10 -> BR[i] = 0;
        vbrain10 -> LR[i] = 99;
        vbrain10 -> pID[i] = i;
        vbrain10 -> vSPIndex[i] = 90;
    }

    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 100; j++)
        {
            vbrain10 -> vStorage[i][j][0] = '#';
            //printf("vStorage[%d][%d][0] = %c\n", i, j, vbrain10 -> vStorage[i][j][0]);
        }
    }
}

/*
 * Allocate memory for the process instructions.
 */
void allocateMemory(char instruction[], BRAIN10 *brain10, VBRAIN10 *vbrain10)
{
    int i;
    int curP = brain10 -> numProcesses - 1;
    int curB = vbrain10 -> vIC[curP];
    for (i = 0; i < 4; i++) {
        char curC = instruction[i];
        brain10 -> storage[8000+(vbrain10 -> BR[curP])+curB][i] = curC;
        //printf("Process %d: [%d][%d] -> %c\n", curP, 8000+(curP*100)+curB, i, curC);
    }
    vbrain10 -> vIC[curP]++;
}

/*
 * Method for executing instructions.
 * 
 * Checks what the instruction is, and then executes it.  Instructions
 * are located in instructions.c
 */
void executeInstruction(int process, char instruction[], BRAIN10 *brain10, VBRAIN10 *vbrain10, MAILMAN *mailman, SEMAPHORES *semaphores)
{
    // Separate operator and operand into separate strings
    char operator[2], operand[2];
    operator[0] = instruction[0];
    operator[1] = instruction[1];
    operand[0] = instruction[2];
    operand[1] = instruction[3];

    
        // If operator starts with 'A'
    if (operator[0] == 'A')
    {
        // Addition
        if (operator[1] == 'D')
            addition(brain10, vbrain10, atoi(operand), process);
        // Stack addition
        else
            stackAdd(brain10, vbrain10, process);
    }
    
    // If operator starts with 'B'
    else if (operator[0] == 'B')
    {
        // Branch conditional
        if (operator[1] == 'T')
            branchConditional(brain10, vbrain10, atoi(operand), process);
        // Branch unconditional
        else
            branchUnconditional(brain10, vbrain10, atoi(operand), process);
    }
    
      // If operator starts with 'C'
    else if (operator[0] == 'C')
    {
        // Compare equals
        if (operator[1] == 'E')
            compareEqual(brain10, vbrain10, atoi(operand), process);
        // Compare less than
        else
            compareLess(brain10, vbrain10, atoi(operand), process);
    }
    
    // If operator starts with 'D'
    else if (operator[0] ==  'D')
    {
        // Division
        if (operator[1] == 'I')
            division(brain10, vbrain10, atoi(operand), process);
        // Stack Division
        else
            stackDivide(brain10, vbrain10, process);
    }
    
        // If the Operator starts with 'G'
    else if (operator[0] ==  'G')
    {
        // Get Process ID
        if (operator[1] == 'P')
            processID(brain10, vbrain10, process);
        // Get Data
        else
            getData(brain10, vbrain10, atoi(operand), process);
    }
    
    // If the Operator starts with 'H'
    else if (operator[0] == 'H')
    {
        // Halts the process
        halt();
    }
    
    // If operator starts with 'L'
    else if (operator[0] == 'L')
    {   
        // Load register low
        if (operator[1] == 'L')
            loadRegLow(brain10, vbrain10, atoi(operand), process);
        
        // Load register
        else if (operator[1] == 'R')
            loadRegister(brain10, vbrain10, atoi(operand), process);
        
        // Load from shared memory
        else if (operator[1] == 'S')
            loadShared(brain10, vbrain10, atoi(operand), process);
        
        // Load register high
        else
            loadRegHigh(brain10, vbrain10, atoi(operand), process);
    }
    
     // If operator starts with 'M'
    else if (operator[0] == 'M')
    {
        // Multiplication
        if (operator[1] == 'U')
            multiplication(brain10, vbrain10, atoi(operand), process);
        // Stack multiplication    
        else
            stackMultiply(brain10, vbrain10, process);
    }
    
    // If Operator starts with 'NP'
    else if (operator[0] == 'N')
    {
        // Sleep the process
        noop();
    }
    
    // If operator starts with 'P'
    else if (operator[0] == 'P')
    {
        // Wait on semaphore
        if (operator[1] == 'E')
            waitSemaphore(brain10, vbrain10, semaphores, atoi(operand), process);
        // Push
        else if (operator[1] == 'H')
            push(brain10, vbrain10, process);
        // Pop
        else if (operator[1] == 'P')
            pop(brain10, vbrain10, process);
        // Load register from stack pointer
        else if (operator[1] == 'S')
            loadRfromSP(brain10, process);
        // Print data
        else
            printData(brain10, vbrain10, atoi(operand), process);
    }
    
    // If operater starts with 'R'
    else if (operator[0] == 'R')
    {
        // Receive message from process a
        receiveMsg(vbrain10, process, operand, mailman);
    }
    
    // If operator starts with 'S'
    else if (operator[0] == 'S')
    {
        // Send Message
        if (operator[1] == 'D')
            sendMsg(vbrain10, process, atoi(operand), mailman);
        
        // Sets value of semaphore to register
        else if (operator[1] == 'I')
            setSemaphore(brain10, vbrain10, semaphores, atoi(operand), process);
        
        // Load stack pointer from register
        else if (operator[1] == 'P')
            loadSPfromReg(brain10, process);
        
        // Load into Memory
        else if (operator[1] == 'R')
            loadMemory(brain10, vbrain10, atoi(operand), process);
        
        // Store into shared memory
        else if (operator[1] == 'T')
            storeShared(brain10, vbrain10, atoi(operand), process);
        
        // Subtraction
        else if (operator[1] == 'U')
            subtraction(brain10, vbrain10, atoi(operand), process);
        
        // Stack subtraction
        else
            stackSubtract(brain10, vbrain10, process);
    }
    
    // If operator starts with 'S'
    else if (operator[0] == 'V')
    {
        // Signal semaphore
        signalSemaphore(brain10, vbrain10, semaphores, atoi(operand), process);
    }
    
    // Invalid Command
    else
        printf("Invalid command! (%s)\n", operator);
}


/*
 * Push a process onto the semaphore wait queue.
 */
void pushReadyProcess(BRAIN10 *brain10, int process)
{
    brain10 -> readyQueue[brain10->in] = process;
    brain10->in++;
    if(brain10-> in >= 100)
        brain10-> in = 0;
}

/*
 * Pop the last process off the semaphore wait queue,
 * allowing them to access shared storage on the next
 * time slice.
 */
int popReadyProcess(BRAIN10 *brain10)
{
    int readyProcess = brain10 -> readyQueue[brain10 -> out];
    brain10 -> out++;
    if(brain10 -> out >= 100)
        brain10 -> out = 0;
    return readyProcess;
}

/*
 * Check if the ready queue is empty.
 */

int isEmpty(BRAIN10 *brain10)
{
    if(brain10 -> out == brain10 -> in)
        return 1;
    else
        return 0;
}
